package main

import (
	"fmt"
	"sync"
)

const (
	PGaiBang  string = "丐帮"
	PMingJiao string = "明教"
)

//-------- 抽象层 -------
type Listener interface {
	OnFriendBeFight(event *Event, wg *sync.WaitGroup)
	GetName() string
	GetParty() string
	Title() string
}

type Notifier interface {
	AddListener(listener Listener)
	RemoveListener(listener Listener)
	Notify(event *Event)
}

type Event struct {
	Noti    Notifier // 被知晓的通知者
	One     Listener  // 事件主动发出者
	Another Listener  // 事件被动接收者
	Msg     string    // 具体消息
}

//-------- 实现层 -------
// 英雄(Listener)
type Hero struct {
	Name  string
	Party string
}

func (hero *Hero) Fight(another Listener, baixiao Notifier) {
	msg := fmt.Sprintf("%s 将 %s 揍了...", hero.Title(), another.Title())
	event := &Event{
		Noti:    baixiao,
		One:     hero,
		Another: another,
		Msg:     msg,
	}
	baixiao.Notify(event)
}

func (hero *Hero) Title() string {
	return fmt.Sprintf("[%s]:%s", hero.Party, hero.Name)
}

func (hero *Hero) OnFriendBeFight(event *Event, wg *sync.WaitGroup) {
	defer wg.Done()

	if hero.Name == event.One.GetName() || hero.Name == event.Another.GetName() {

		fmt.Println("非成员, 退出")
		return
	}

	if hero.Party == event.One.GetParty() {
		fmt.Println(hero.Title(), "得知消息，拍手叫好！！！")
	} else if hero.Party == event.Another.GetParty() {
		fmt.Println(hero.Title(), "得知消息，发起报仇反击！！！")
		hero.Fight(event.One, event.Noti)
	}
}

func (hero *Hero) GetName() string {
	return hero.Name
}

func (hero *Hero) GetParty() string {
	return hero.Party
}

// 百晓生(Notifier)
type BaiXiao struct {
	heroList []Listener
	events   chan *Event
	wg       sync.WaitGroup
}

func NewBaiXiao() *BaiXiao {
	b := &BaiXiao{
		events: make(chan *Event, 100),
	}
	b.start()
	return b
}

func (b *BaiXiao) start() {
	for event := range b.events {
		fmt.Println("【世界消息】 百晓生广播消息: ", event.Msg)
		for _, listener := range b.heroList {
			b.wg.Add(1)
			listener.OnFriendBeFight(event, &b.wg)
		}
	}
}

func (b *BaiXiao) AddListener(listener Listener) {
	b.heroList = append(b.heroList, listener)
}

func (b *BaiXiao) RemoveListener(listener Listener) {
	for index, l := range b.heroList {
		if listener == l {
			b.heroList = append(b.heroList[:index], b.heroList[index+1:]...)
			break
		}
	}
}

func (b *BaiXiao) Notify(event *Event) {
	b.events <- event
}

func (b *BaiXiao) Close() {
	close(b.events) // Close the events channel to stop the start goroutine
	b.wg.Wait()     // Wait for all notifications to be processed
}

func main() {
	hero1 := Hero{"黄蓉", PGaiBang}
	hero2 := Hero{"洪七公", PGaiBang}
	hero3 := Hero{"乔峰", PGaiBang}
	hero4 := Hero{"张无忌", PMingJiao}
	hero5 := Hero{"韦一笑", PMingJiao}
	hero6 := Hero{"金毛狮王", PMingJiao}

	baixiao := NewBaiXiao()

	baixiao.AddListener(&hero1)
	baixiao.AddListener(&hero2)
	baixiao.AddListener(&hero3)
	baixiao.AddListener(&hero4)
	baixiao.AddListener(&hero5)
	baixiao.AddListener(&hero6)

	fmt.Println("武林一片平静.....")
	hero1.Fight(&hero5, baixiao)

	baixiao.Close() // Ensure all notifications are processed before exiting
}
